package studio.mandysa.jiuwo.adapter

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import studio.mandysa.jiuwo.diff.JiuWoDiffItemCallback


@Suppress("UNCHECKED_CAST")
class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.BindingViewHolder<Any>>() {

    companion object {
        @JvmStatic
        val COMPOSE_VIEW_ID: Int = View.generateViewId()
    }

    /**
     * JiuWo以单元来存储对应model的view构造、bind事件
     * [id]和[createViewScope]是[RecyclerAdapter]构造[BindingViewHolder]的关键，具体请参看[onCreateViewHolder]
     * 想知道[BindingViewHolder.model]是如何与[BindingViewHolder]绑定的请参看[onBindViewHolder]
     */
    class ItemUnit(
        val id: Int,
        val createViewScope: (parent: ViewGroup, viewType: Int) -> View,
        val bindScope: (BindingViewHolder<Any>.() -> Unit)?
    )

    /**
     * 实现JiuWo局部刷新的核心
     * 在[headers]、[models]、[footers]更新后，通过[mMergedLists]获得三个列表的集合，然后通过[mDiffer]的[AsyncListDiffer.submitList]方法提交更新后列表
     * [AsyncListDiffer]会根据[JiuWoDiffItemCallback]中的逻辑判断差异，从而去进行局部刷新
     */
    private val mDiffer = AsyncListDiffer(this, JiuWoDiffItemCallback)

    /**
     * 返回[headers]、[models]、[footers]的集合列表
     */
    private val mMergedLists: List<Any>
        get() {
            return arrayListOf<Any>().also { list ->
                headers?.let {
                    list.addAll(it)
                }
                models?.let {
                    list.addAll(it)
                }
                footers?.let {
                    list.addAll(it)
                }
            }
        }

    /**
     * 头部项目列表
     * 此列表中的数据将显示在[models]、[footers]之前
     */
    var headers: List<Any>? = null
        set(value) {
            value ?: return
            field = value
            mDiffer.submitList(mMergedLists)
        }

    /**
     * 默认项目列表
     * 此列表中的数据将显示在[headers]、[footers]之间
     */
    var models: List<Any>? = null
        set(value) {
            value ?: return
            field = value
            mDiffer.submitList(mMergedLists)
        }

    /**
     * 页脚项目列表
     * 此列表中的数据将显示在[models]之后
     */
    var footers: List<Any>? = null
        set(value) {
            value ?: return
            field = value
            mDiffer.submitList(mMergedLists)
        }

    /**
     * 返回[headers]的大小
     */
    val headerSize get() = headers?.size ?: 0

    /**
     * 返回[models]的大小
     */
    val modelSize get() = models?.size ?: 0

    /**
     * 返回[footers]的大小
     */
    val footerSize get() = footers?.size ?: 0

    /**
     * 存储[addView]等函数中添加的模型类型及其对应的唯一识别id(也有可能是布局id)
     * @see [addView]
     * @see [addType]
     * @see [addViewBinding]
     * @see [addCompose]
     * @see [getItemViewType]
     */
    val type = HashMap<Class<*>, ItemUnit>()

    /**
     * 添加模型类型及其布局id
     * @param M model类型
     * @param id layoutId
     * @param bindScope 在此范围中绑定你的[BindingViewHolder]
     * @see [type]
     */
    inline fun <reified M> addType(
        @LayoutRes id: Int,
        crossinline bindScope: (BindingViewHolder<M>.() -> Unit) = {}
    ) {
        type[M::class.java] = ItemUnit(id, { parent: ViewGroup, viewType: Int ->
            LayoutInflater.from(parent.context).inflate(viewType, parent, false)
        }, {
            bindScope.invoke(this as BindingViewHolder<M>)
        })
    }

    /**
     * [addType]不满足要求时使用
     * @param M model类型
     * @param createViewScope 在范围中返回您的自定义视图
     * @param bindScope 在此范围中绑定你的[BindingViewHolder]
     * @see [addViewBinding]
     */
    inline fun <reified M> addView(
        crossinline createViewScope: ViewGroup.() -> View,
        crossinline bindScope: (BindingViewHolder<M>.() -> Unit) = {}
    ) {
        type[M::class.java] = ItemUnit(View.generateViewId(), { parent, _ ->
            createViewScope.invoke(parent)
        }, {
            bindScope.invoke(this as BindingViewHolder<M>)
        })
    }

    /**
     * [ViewBinding]专供，PS：编译时请不要混淆[ViewBinding]的派生类！
     * @param M model类型
     * @param bindScope 在此范围中绑定你的[BindingViewHolder],此范围额外提供了[VB]
     */
    inline fun <reified M, reified VB : ViewBinding> addViewBinding(
        crossinline bindScope: BindingViewHolder<M>.(VB) -> Unit = {}
    ) {
        addView<M>({
            val binding = VB::class.java.getMethod(
                "inflate",
                LayoutInflater::class.java,
                ViewGroup::class.java,
                Boolean::class.java
            )
            (binding.invoke(null, LayoutInflater.from(context), this, false) as VB).root
        }) {
            val binding = VB::class.java.getMethod(
                "bind",
                View::class.java
            )
            bindScope.invoke(this, binding.invoke(null, itemView) as VB)
        }
    }

    @Deprecated("RecyclerView 和 Compose 容易出现抖动和丢帧。我暂时找不到解决办法，所以暂时废弃这个函数。")
    inline fun <reified M> addCompose(
        crossinline composeScope: @Composable BindingViewHolder<M>.() -> Unit = {}
    ) {
        type[M::class.java] = ItemUnit(COMPOSE_VIEW_ID, { parent, _ ->
            ComposeView(parent.context).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
            }
        }) {
            val composeView = itemView as ComposeView
            onRecycled {
                composeView.disposeComposition()
            }
            composeView.setContent { composeScope.invoke(this as BindingViewHolder<M>) }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder<Any> {
        type.forEach { entry ->
            if (entry.value.id == viewType) {
                return BindingViewHolder(entry.value.createViewScope.invoke(parent, viewType))
            }
        }
        throw NullPointerException("未知类型，此Model可能没有正确添加到JioWo")
    }

    override fun onBindViewHolder(holder: BindingViewHolder<Any>, position: Int) {
        val model = mDiffer.currentList[position]
        holder.mModel = model
        type[model::class.java]?.bindScope?.invoke(holder)
    }

    override fun onViewAttachedToWindow(holder: BindingViewHolder<Any>) {
        super.onViewAttachedToWindow(holder)
        holder.onAttached?.invoke(holder)
    }

    override fun onViewDetachedFromWindow(holder: BindingViewHolder<Any>) {
        super.onViewDetachedFromWindow(holder)
        holder.onDetached?.invoke(holder)
    }

    override fun onViewRecycled(holder: BindingViewHolder<Any>) {
        super.onViewRecycled(holder)
        holder.onRecycled?.invoke(holder)
    }

    override fun getItemCount(): Int {
        return mDiffer.currentList.size
    }

    override fun getItemViewType(position: Int): Int {
        return type[mDiffer.currentList[position]::class.java]?.id!!
    }

    /**
     * 清除[headers],[models],[footers]的所有数据，并执行[notifyDataSetChanged]
     */
    @SuppressLint("NotifyDataSetChanged")
    fun clearModels() {
        headers = null
        models = null
        footers = null
        notifyDataSetChanged()
    }

    inner class BindingViewHolder<M>(itemView: View) :
        RecyclerView.ViewHolder(itemView) {

        var mModel: M? = null

        /**
         * 与此[BindingViewHolder]绑定的model
         */
        val model: M
            get() = mModel as M

        internal var onAttached: (BindingViewHolder<M>.() -> Unit)? = null

        internal var onDetached: (BindingViewHolder<M>.() -> Unit)? = null

        internal var onRecycled: (BindingViewHolder<M>.() -> Unit)? = null

        /**
         * 被 [onViewAttachedToWindow] 时会调用 [block]
         */
        fun onAttached(block: BindingViewHolder<M>.() -> Unit) {
            onAttached = block
        }

        /**
         * 被 [onViewDetachedFromWindow] 时会调用 [block]
         */
        fun onDetached(block: BindingViewHolder<M>.() -> Unit) {
            onDetached = block
        }

        /**
         * 被 [onViewRecycled] 时会调用 [block]
         */
        fun onRecycled(block: BindingViewHolder<M>.() -> Unit) {
            onRecycled = block
        }

        /**
         * 返回[model]所在的列表,[headers]、[models]、[footers]其中之一
         */
        val models: List<Any>?
            get() = if (layoutPosition < headerSize) {
                headers
            } else
                if (layoutPosition >= headerSize && layoutPosition < modelSize + headerSize) {
                    this@RecyclerAdapter.models
                } else {
                    footers
                }

        /**
         * 返回model在当前列表中的位置。
         * @see model
         */
        val modelPosition
            get():Int {
                return if (layoutPosition < headerSize) {
                    layoutPosition
                } else
                    if (layoutPosition >= headerSize && layoutPosition < modelSize + headerSize) {
                        layoutPosition - headerSize
                    } else {
                        (layoutPosition - headerSize - modelSize)
                    }
            }
    }

}